//
//   Copyright 2011 Gynvael Coldwind & Mateusz "j00ru" Jurczyk
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
#include <cstdio>
#include <cstdlib>
#include <string>
#include <string.h>
#include <stdint.h>
#include <map>
#include "j00ru.h"
#include "global.h"
using namespace std;


map<string, enum TILE_TYPE> itemTranslationMap;
map<char, TILE_STRUCT*> charTranslationMap;


LEVEL_STRUCT* game_LoadLevel()
{
  char filename[256];

  snprintf(filename, sizeof(filename), "%u.txt", g_GameState.levelNumber);
  return readLevelFromFile(filename);
}



bool skipMap(FILE* f)
{
  static char buffer[1024];
  int width, height;

  // skip the w, h map descriptors
  if(fgets(buffer, sizeof(buffer), f) == NULL)
    return false;
  if(sscanf(buffer, "%i%i", &width, &height) < 2)
    return false;

  for( int i = 0; i < height; i++ )
    fgets(buffer, sizeof(buffer), f);

  return true;
}

void initTranslationTable(char* levelFile)
{
  static char buffer[1024];
  FILE* f = fopen(levelFile, "r");
  int mapCount;
  char* ptr;

  if(f == NULL)
    return;


  //
  // Init the item translation map
  //
  itemTranslationMap[string("wall")]          = TILE_WALL;
  itemTranslationMap[string("floor")]         = TILE_FLOOR;
  itemTranslationMap[string("door_v")]        = TILE_DOOR_V;
  itemTranslationMap[string("door_h")]        = TILE_DOOR_H;  
  itemTranslationMap[string("start")]         = TILE_START_POINT;
  itemTranslationMap[string("end")]           = TILE_END_POINT;
  itemTranslationMap[string("hole")]          = TILE_HOLE;
  itemTranslationMap[string("nothing")]       = TILE_NOTHING;
  itemTranslationMap[string("std_button")]    = TILE_STD_BUTTON;
  itemTranslationMap[string("toggle_button")] = TILE_TOGGLE_BUTTON;
  itemTranslationMap[string("exit")]          = TILE_EXIT;
  itemTranslationMap[string("teleport")]      = TILE_TELEPORT;

  if(fgets(buffer, sizeof(buffer), f) == NULL || sscanf(buffer, "%i", &mapCount) < 1)
    return;
  if(fgets(buffer, sizeof(buffer), f) == NULL)
    return;
  if(fgets(buffer, sizeof(buffer), f) == NULL)
    return;

  while(mapCount--)
  {
    if(!skipMap(f))
    {
      fclose(f);
      return;
    }
  }

  // Read all of the lines after map descriptors (the legend)
  while(fgets(buffer, sizeof(buffer), f))
  {
    TILE_STRUCT* tile = new TILE_STRUCT;
    if(tile == NULL)
    {
      puts("error: unable to allocate memory for TILE_STRUCT");
      fclose(f);

      return;
    }

    memset(tile, 0, sizeof(TILE_STRUCT));
    tile->params[0] = 0;

    ptr = strchr(buffer, '\n');
    if(ptr) *ptr = '\0';
    ptr = strchr(buffer, '\r');
    if(ptr) *ptr = '\0';

    if(buffer[0] == '\0')
    {
      delete tile;
      continue;
    }

    if(buffer[1] != '=' || buffer[2] == '\0')
    {
      printf("error: invalid legenda line:\n%s\n", buffer);
      fclose(f);

      return;
    }

    ptr = strchr(buffer, ',');
    if(ptr) *ptr = '\0';
    
    if(itemTranslationMap.find(string(&buffer[2])) == itemTranslationMap.end())
    {
      printf("error: could not find item \"%s\" in the translation table\n", &buffer[2]);
      fclose(f);
      
      return;
    }

    tile->tileType = itemTranslationMap[string(&buffer[2])];
    
    ptr = buffer + strlen(buffer) + 1;
    sscanf(ptr, "%d,%d,%d,%d", &tile->params[0], &tile->params[1], &tile->params[2], &tile->params[3]);

    charTranslationMap[buffer[0]] = tile;
  }

  fclose(f);
}

bool convertCharToTile(char* levelFile, char c, TILE_STRUCT* tile)
{
  initTranslationTable(levelFile);

  if(charTranslationMap.find(c) == charTranslationMap.end())
  {
    printf("error: unknown tile char=%c\n", c);
    return false;
  }

  memcpy(tile, charTranslationMap[c], sizeof(TILE_STRUCT));
  return true;
}


MAP_STRUCT* readMapFromFile(char* filename, int id, int* maps, int* players, float* deadline)
{
  static char buffer[1024];
  FILE* f;
  int mapCount;
  int playerCount;
  int mapWidth, mapHeight;
  float expiration;
  TILE_STRUCT** ret;

  f = fopen(filename, "r");
  if(f == NULL)
  {
    printf("error: unable to open the \"%s\" map file\n", filename);
    return NULL;
  }

  
  if(fgets(buffer, sizeof(buffer), f) == NULL || sscanf(buffer, "%i", &mapCount) < 1)
  {
    puts("error: unable to read the map count from level file");
    fclose(f);

    return NULL;
  }
  if(fgets(buffer, sizeof(buffer), f) == NULL || sscanf(buffer, "%i", &playerCount) < 1)
  {
    puts("error: unable to read the player count from level file");
    fclose(f);

    return NULL;
  }
  if(fgets(buffer, sizeof(buffer), f) == NULL || sscanf(buffer, "%f", &expiration) < 1)
  {
    puts("error: unable to read the player deadline from level file");
    fclose(f);

    return NULL;
  }

  if(id >= mapCount)
  {
    printf("error: cannot provide map id=%u, as the file hosts only %u maps.\n", id, mapCount);
    fclose(f);
    
    return NULL;
  }

  // skip the necessary amount of maps
  for( int i = 0; i < id; i++ )
  {
    if(!skipMap(f))
    {
      printf("error: unable to skip map id=%u (probably doesn't exist?)", i);
      fclose(f);

      return NULL;
    }
  }

  // read the map in consideration
  fgets(buffer, sizeof(buffer), f);
  sscanf(buffer, "%i%i", &mapWidth, &mapHeight);
  if(mapWidth >= (int)sizeof(buffer))
  {
    printf("error: map width (%u) is larger than local buffer size (%u)\n", mapWidth, sizeof(buffer));
    fclose(f);

    return NULL;
  }

  // Allocate memory for output table
  ret = new TILE_STRUCT*[mapHeight];
  if(ret == NULL)
  {
    printf("error: unable to allocate memory for %u TILE_STRUCT pointers\n", mapHeight);
    fclose(f);

    return NULL;
  }

  for( int i = 0; i < mapHeight; i++ )
  {
    char* ptr;
    TILE_STRUCT* tileRow = new TILE_STRUCT[mapWidth];
    if(tileRow == NULL)
    {
      printf("error: unable to allocate memory for %u TILE_STRUCT structures", mapWidth);
      fclose(f);
      delete ret;

      return NULL;
    }

    if(fgets(buffer, sizeof(buffer), f) == NULL)
    {
      printf("error: unable to read line %u of map", i);
      fclose(f);
      delete ret;

      return NULL;
    }

    ptr = strchr(buffer, '\n');
    if(ptr) *ptr = '\0';
    ptr = strchr(buffer, '\r');
    if(ptr) *ptr = '\0';

    for( int j = 0; buffer[j] != '\0'; j++ )
    {
      if(convertCharToTile(filename, buffer[j], &tileRow[j]) == false)
      {
        printf("error: unable to convert row[%u] character\n", j);
        fclose(f);
        delete ret;

        return NULL;
      }

      if(tileRow[j].tileType != TILE_START_POINT)
      {
        // Manage specific types of tiles (copy their settings)
        if(tileRow[j].params[0] != -1)
        {
          g_ObjectParams[tileRow[j].params[0]][0] = tileRow[j].params[1];
          g_ObjectParams[tileRow[j].params[0]][1] = tileRow[j].params[2];
          g_ObjectParams[tileRow[j].params[0]][2] = tileRow[j].params[3];
        }
      }
    }

    ret[i] = tileRow;
  }

  if(maps != NULL)
    *maps = mapCount;
  if(players != NULL)
    *players = playerCount;
  if(deadline != NULL)
    *deadline = expiration;

  MAP_STRUCT* map = new MAP_STRUCT;
  map->width = mapWidth;
  map->height = mapHeight;
  map->tiles = ret;
 
  fclose(f);
  return map;
}

LEVEL_STRUCT* readLevelFromFile(char* filename)
{
  LEVEL_STRUCT* level = new LEVEL_STRUCT;
  MAP_STRUCT* currentMap;

  if(level == NULL)
  {
    puts("error: unable to allocate memory for LEVEL_STRUCT");
    return NULL;
  }

  currentMap = readMapFromFile(filename, 0, &level->mapCount, &level->playerCount, &level->deadline);
  if(currentMap == NULL)
    return NULL;

  level->maps = new MAP_STRUCT*[level->mapCount];
  level->maps[0] = currentMap;
  
  for( int i = 1; i < level->mapCount; i++ )
  {
    currentMap = readMapFromFile(filename, i, NULL, NULL, NULL);
    if(currentMap == NULL)
    {
      delete level->maps;
      delete level;
      return NULL;
    }

    level->maps[i] = currentMap;
  }

  level->activeMap = 0; // XXX: this is assumed to always be true

  return level;
}

#ifndef FINAL_BUILD
int main(int argc, char** argv)
{
  int mapCount;

  if(argc != 2)
  {
    printf("Usage: %s <map file>\n", argv[0]);
    return 1;
  }

  if(readLevelFromFile(argv[1]) == NULL)
  {
    puts("error: unable to read level from input file");
    return 1;
  }

  puts("Successfully read the map file");

  return 0;
}
#endif

